// ossproxy project main.go
package main

import (
	"crypto/tls"
	"fmt"
	"log"
	"net/http"
	"net/http/httputil"
	"net/url"
	"regexp"
	"strings"
)

type handle struct {
	proxy *httputil.ReverseProxy
	host  string

	servers []*Server
}

const RouterMode_Map int = 1
const RouterMode_Fun int = 2
const RouterMode_Prefix int = 3
const RouterMode_Suffix int = 4
const RouterMode_PrefixAndSuffix int = 5
const RouterMode_Regular int = 6

type PathRoter struct {
	mode    int
	keys    []string
	pathMap map[string]string
	handler func(p string) bool
}

type Server struct {
	proxy      *httputil.ReverseProxy
	targetUrl  string
	serverName string
	inputHost  string

	routers       []PathRoter
	prefixRewrite map[string]string

	delHeaders []string

	setHeaders map[string]string
}

func prefix(pre string) PathRoter {
	return PathRoter{
		mode: RouterMode_Prefix,
		keys: []string{
			pre,
		},
	}
}
func suffix(suf string) PathRoter {
	return PathRoter{
		mode: RouterMode_Suffix,
		keys: []string{
			suf,
		},
	}
}
func prefixAndSuffix(pre, suf string) PathRoter {
	return PathRoter{
		mode: RouterMode_PrefixAndSuffix,
		keys: []string{
			pre,
			suf,
		},
	}
}

func fun(handler func(p string) bool) PathRoter {
	return PathRoter{
		mode:    RouterMode_Fun,
		handler: handler,
	}
}

func mapper(ma map[string]string) PathRoter {
	return PathRoter{
		mode:    RouterMode_Map,
		pathMap: ma,
	}
}
func regular(r string) PathRoter {
	return PathRoter{
		mode: RouterMode_Regular,
		keys: []string{r},
	}
}

func _prefix(router *PathRoter, w http.ResponseWriter, r *http.Request) bool {
	path := r.URL.Path

	return len(path) >= len(router.keys[0]) &&
		path[:len(router.keys[0])] == router.keys[0]
}
func _suffix(router *PathRoter, w http.ResponseWriter, r *http.Request) bool {
	path := r.URL.Path

	return len(path) >= len(router.keys[0]) &&
		path[len(path)-len(router.keys[0]):] == router.keys[0]
}

func _prefixAndSuffix(router *PathRoter, w http.ResponseWriter, r *http.Request) bool {
	path := r.URL.Path

	return len(path) >= len(router.keys[0])+len(router.keys[1]) &&
		path[:len(router.keys[0])] == router.keys[0] &&
		path[len(path)-len(router.keys[1]):] == router.keys[1]

	matchSuffix := func() bool {
		if len(router.keys) == 1 {
			return true
		}
		return len(path) >= len(router.keys[1]) && path[len(path)-len(router.keys[1]):] == router.keys[1]

	}

	return (router.keys[0] != "" || (len(path) >= len(router.keys[0]) && path[len(path)-len(router.keys[0]):] == router.keys[0])) &&
		matchSuffix()

}
func _map(router *PathRoter, w http.ResponseWriter, r *http.Request) bool {
	revpath, ok := router.pathMap[r.URL.Path]
	if ok {
		r.URL.Path = revpath
		return true
	}
	return false
}
func _regular(router *PathRoter, w http.ResponseWriter, r *http.Request) bool {
	m, _ := regexp.Match(router.keys[0], []byte(r.URL.Path))
	return m
}
func _fun(router *PathRoter, w http.ResponseWriter, r *http.Request) bool {
	return router.handler(r.URL.Path)
}

func (this *handle) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	for _, s := range this.servers {
		matched := false
		for _, router := range s.routers {
			switch router.mode {
			case RouterMode_Prefix:
				matched = _prefix(&router, w, r)
				break
			case RouterMode_Suffix:
				matched = _suffix(&router, w, r)
				break
			case RouterMode_PrefixAndSuffix:
				matched = _prefixAndSuffix(&router, w, r)
				break
			case RouterMode_Map:
				matched = _map(&router, w, r)
				break
			case RouterMode_Regular:
				matched = _regular(&router, w, r)
				break
			case RouterMode_Fun:
				matched = _fun(&router, w, r)
				break
			default:
				panic("error router mode")
			}
			if matched {
				break
			}
		}

		if matched {
			if s.inputHost != "" {
				r.Host = s.inputHost
			}

			if s.prefixRewrite != nil {
				for k, v := range s.prefixRewrite {
					if len(k) <= len(r.URL.Path) && r.URL.Path[:len(k)] == k {
						r.URL.Path = v + r.URL.Path[len(k):]
						break
					}
				}
			}

			s.proxy.ServeHTTP(w, r)
			break
		}

	}

	// w.Write([]byte("error path!"))
}

func main() {
	// remote, _ := url.Parse("http://wechat-media-data.oss-cn-hangzhou.aliyuncs.com")
	listen := ":80"
	svrs := []*Server{
		// &Server{
		// 	targetUrl:  "http://www.baidu.com",
		// 	serverName: "_",
		// 	inputHost:  "www.baidu.com",
		// 	routers: []PathRoter{
		// 		PathRoter{
		// 			mode: RouterMode_PrefixAndSuffix,
		// 			keys: []string{
		// 				"/",
		// 			},
		// 		},
		// 	},

		// 	prefixRewrite: map[string]string{
		// 		"/rr": "",
		// 	},

		// 	delHeaders: []string{
		// 		"Content-Disposition",
		// 	},

		// 	setHeaders: map[string]string{},
		// },

		{
			targetUrl:  "https://inews.gtimg.com",
			serverName: "_",
			inputHost:  "inews.gtimg.com",
			routers: []PathRoter{
				prefix("/"),
				fun(func(path string) bool {
					jspurls := []string{"/web/",
						"/login/",
						"/article/",
						"/postmoney/",
						"/login"}
					for _, p := range jspurls {
						if strings.Index(path, p) == 0 {
							return false
						}
					}

					if strings.Index(path, "/jsp") == 0 {

						if string(path[len(path)-4:]) != ".jsp" && string(path[len(path)-3:]) != ".js" {
							return false
						}
					}
					return true
				}),
			},

			// prefixRewrite: map[string]string{
			// 	"/rr/": "/",
			// },

			//setHeaders: map[string]string{
			//	// "Content-Disposition": "attachment; filename=foobar.jpg",
			//	"Content-Disposition": "inline;",
			//},
		},

		// &Server{
		// 	targetUrl:  "http://www.baidu.com",
		// 	serverName: "_",
		// 	inputHost:  "www.baidu.com",
		// 	prefixAndSuffix: []MatchPair{
		// 		MatchPair{
		// 			prefix: "/",
		// 			suffix: "",
		// 		},
		// 	},
		// 	// regular: []string{},
		// 	// pathRoter: func(p string) bool {
		// 	// 	return true
		// 	// },
		// 	// prefixRewrite: map[string]string{
		// 	// 	"/rr": "",
		// 	// },

		// 	// delHeaders: []string{
		// 	// 	"Content-Disposition",
		// 	// },

		// 	// setHeaders: map[string]string{},
		// },
	}

	for _, s := range svrs {
		remote, _ := url.Parse(s.targetUrl)
		s.proxy = httputil.NewSingleHostReverseProxy(remote)
		s.proxy.Transport = &http.Transport{ //解决x509: certificate signed by unknown authority
			TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
		}
		if s.delHeaders != nil || s.setHeaders != nil {
			s.proxy.ModifyResponse = func(resp *http.Response) error {
				if s.delHeaders != nil {
					for _, v := range s.delHeaders {
						resp.Header.Del(v)
					}
				}
				if s.setHeaders != nil {
					for k, v := range s.setHeaders {
						resp.Header.Set(k, v)
					}
				}
				// resp.Header.Set("key", "value")
				return nil
			}
		}
	}

	// remote, _ := url.Parse("http://www.baidu.com")

	// proxy := httputil.NewSingleHostReverseProxy(remote)

	// proxy.ModifyResponse = func(resp *http.Response) error {
	// 	resp.Header.Del("Content-Disposition")
	// 	// resp.Header.Set("key", "value")
	// 	return nil
	// }

	h := &handle{
		servers: svrs,
	}
	err := http.ListenAndServe(listen, h)
	if err != nil {
		log.Fatalln("ListenAndServe: ", err)
	}
	fmt.Println("Hello World!")
}
